#!/usr/bin/env ruby
# -*- coding: us-ascii -*-
# Elizabeth Wiethoff 2008

raise NotImplementedError, 'need >= 1.8' if  RUBY_VERSION < '1.8'

require 'fileutils'
require 'pp' if $DEBUG
require 'yaml'

require File.join(File.dirname(__FILE__), 'openlock')

########################################

class YAMLStore
  def initialize(glob)
    @data = Hash.new
    
    unless $LOADED_FEATURES.find { |lib| lib =~ /yaml/i } 
      @path = nil
      return
    end
    
    path = File.expand_path glob
    dir = File.dirname path
    
    begin
      FileUtils.mkdir_p(dir) unless File.exist?(dir)
      FileUtils.touch(path) unless File.exist?(path)
    rescue SystemCallError
      @path = nil
      return
    end
    
    @path = (File.readable?(path) and File.writable?(path)) ? 
      path : nil
  end
  
  attr_reader :data, :path
  protected :data
  
  def get_data_from_file
    return @data unless @path
    ydata = open_lock(@path, 'r') { |f| YAML.load(f) }
    @data = ydata ? ydata : {}
  end
  private :get_data_from_file
  
  def [](key)
    get_data_from_file
    @data[key]
  end
  alias_method :fetch, :[]
  
  def []=(key, obj)
    @data[key] = obj
    return obj unless @path
    result = open_lock(@path, 'w') { |f| YAML.dump(@data, f) }
    result ? obj : nil
  end
  alias_method :store, :[]=
  
  def ==(other)
    get_data_from_file
    @data == other.data and @path == other.path
  end
  
  def each_key
    get_data_from_file
    @data.each_key {|key| yield key }
  end
  
  def each_pair
    get_data_from_file
    @data.each_key {|key| yield key, self[key] }
  end
  
  def empty?
    get_data_from_file
    @data.empty?
  end
  
  def has_key?(key)
    get_data_from_file
    @data.has_key? key
  end
  alias_method :include?, :has_key?
  
  def keys
    get_data_from_file
    @data.keys
  end
  
  def size
    get_data_from_file
    @data.size
  end
  
  def to_a
    get_data_from_file
    @data.to_a
  end
  
  def to_hash
    get_data_from_file
    @data
  end
  
  def to_s
    get_data_from_file
    @data.inspect
  end
  
  def to_yaml
    get_data_from_file
    @data.to_yaml
  end
end

########################################

if $0 == __FILE__
  testfile = '~/superpref/supertest.yaml'
  FileUtils.rm_f(File.expand_path(testfile))  # delete if existing
  
  ys = YAMLStore.new testfile
  p ys.path
  
  p ys['position']  #=> nil, because supertest.yaml is empty
  ys['position'] = {'x'=>50, 'y'=>50}
  p ys['position']  #=> {"x"=>50, "y"=>50}
  p ys  #=> @data={"position"=>{"x"=>50, "y"=>50}}
  
  p ys.fetch('extensions')  #=> nil, because not defined yet
  ys.store 'extensions', %w'wpd pdf rtf txt html htm'
  p ys.fetch('extensions')  #=> ["wpd", "pdf", "rtf", "txt", "html", "htm"]
  p ys  #=> both extensions and position data
  
  puts
  
  p ys.to_a
  p ys.to_hash
  puts ys.to_s
  puts ys.to_yaml
  puts
  
  p ys.size
  p ys.keys unless ys.empty?
  p ys.include?('position')
  p ys.include?('size')
  puts
  
  ys.each_key { |key| puts "#{key}: #{ys[key].inspect}" }
  ys.each_pair { |key, value| puts "#{key}: #{value.inspect}" }
end
